/*
 * Copyright 2023 devaper
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.github.devaper.pigeon.support

import io.github.devaper.pigeon.component.*
import io.github.devaper.pigeon.exception.*
import io.github.devaper.pigeon.handler.method.*
import io.github.devaper.pigeon.request.*
import io.github.devaper.pigeon.response.*
import java.util.concurrent.*
import java.util.concurrent.atomic.*

/**
 * 基于 [CountDownLatch] 实现的 [NotifyDeferred]
 *
 * 其中获取响应结果时采用了默认的超时时间, 避免造成死锁
 *
 * @param methodHandler [AbstractHttpMethodHandler]
 * @param arguments 调用方法时提供的参数, 传到这里是为了调用fallback使用
 * @param timeout 默认的等待超时时间
 * @param unit 默认的等待超时时间单位
 */
class CountDownLatchDeferred<T>(
    private val methodHandler: AbstractHttpMethodHandler,
    private val arguments: Array<out Any?>,
    private val timeout: Long = 1,
    private val unit: TimeUnit = TimeUnit.MINUTES
) : NotifyDeferred<T> {

    /**
     * 本次的请求模板
     */
    internal lateinit var requestTemplate: RequestTemplate

    /**
     * 本次请求绑定的回调
     */
    internal lateinit var callback: HttpClientCallback

    private lateinit var countDownLatch: CountDownLatch
    private lateinit var retryCounter: AtomicInteger

    private var data: T? = null
    private var throwable: Throwable? = null

    init {
        refresh()
    }

    override fun get() = getWithTimeout(timeout, unit)

    @Suppress("unchecked_cast")
    override fun getWithTimeout(timeout: Long, unit: TimeUnit): T {
        try {
            // 检查是否需要等待, 需要则使用超时等待避免死锁
            if (isValidCountDownLatch() && !countDownLatch.await(timeout, unit)) {
                throw CountDownLatchTimeoutException()
            }
            // 出现异常, 进行重试
            // 只有请求失败的异常会进行重试
            val throwable = this.throwable
            if (throwable != null) {
                // 不是请求失败异常, 直接抛出
                if (throwable !is HttpRequestFailureException) {
                    throw throwable
                }
                // 将真实的失败异常取出, 抛异常以它为准而不是外面的壳
                this.throwable = throwable.getWrappedException()

                // 重试
                // 首先检查是否开启重试了, 没开启直接抛出异常
                if (!isEnabledRetry()) {
                    throw this.throwable!!
                }
                try {
                    // 先执行重试计次, 再刷新数据
                    // 避免重试出错时原先的异常已经被清理了, 外部接收不到真正的异常
                    decrementRetryCounter()
                    refresh()
                    methodHandler.httpClientEngine.execute(requestTemplate = requestTemplate, callback = callback)
                    return get()
                } catch (e: Throwable) {
                    // 出现异常直接抛出不再进行重试
                    // 只要满足重试的要求那么就不会抛出异常走到这里
                    throw e
                }
            }
            return data as T
        } catch (e: Throwable) {
            // 尝试调用fallback
            methodHandler.fallbackInstance?.let {
                val deferred = methodHandler.methodContext.method.invoke(it, *arguments) as Deferred<Any>
                return deferred.get() as T
            }
            // 优先抛出自身存储的异常, 抛出的异常可能不是真正有效的错误信息(可能是超时/重试上限等异常)
            throw this.throwable ?: e
        }
    }

    /**
     * 刷新当前所有的数据, 准备好下一次使用此对象
     */
    private fun refresh() {
        data = null
        throwable = null
        countDownLatch = CountDownLatch(1)
    }

    /**
     * 检查是否启用重试
     */
    private fun isEnabledRetry(): Boolean {
        val retryConfig = methodHandler.retryConfig
        if (retryConfig != null) {
            return retryConfig.enabled && retryConfig.attemptCount > 0
        }
        return false
    }

    /**
     * 检查是否为有效的CountDownLatch
     */
    private fun isValidCountDownLatch(): Boolean {
        return ::countDownLatch.isInitialized && countDownLatch.count > 0
    }

    /**
     * 递减重试次数, 达到最大重试次数时抛出异常
     */
    private fun decrementRetryCounter() {
        if (!::retryCounter.isInitialized) {
            retryCounter = AtomicInteger(methodHandler.retryConfig!!.attemptCount)
        }
        if (retryCounter.decrementAndGet() < 0) {
            throw RetryException()
        }
    }


    override fun success(data: T?) {
        this.data = data
        if (isValidCountDownLatch()) {
            this.countDownLatch.countDown()
        }
    }

    override fun failure(throwable: Throwable) {
        this.throwable = throwable
        if (isValidCountDownLatch()) {
            this.countDownLatch.countDown()
        }
    }
}